/*
 * Copyright (C) 2010 Fores Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package jp.fores.midori.xmlrpc;

import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;


/**
 * XML-RPCの構文解析を行う全てのクラスの親クラスとなる抽象クラス。<br>
 * (このクラスはスレッドセーフではありません。)<br>
 * (ライブラリの使用者には公開したくない内部処理用のクラスなので、あえてパッケージスコープにしています。)<br>
 */
abstract class AbstractXmlRpcParser extends DefaultHandler {
	//==========================================================
	//フィールド

	/**
	 * タグの階層レベル
	 */
	protected int level = 0;

	/**
	 * 結果のオブジェクト
	 */
	private Object result;

	/**
	 * ロケータ
	 */
	private Locator locator;


	//==========================================================
	//メソッド

	//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
	//クラスメソッド

	/**
	 * 文字配列の使用範囲に空白文字以外が含まれていないかどうかを判断します。<br>
	 *
	 * @param ch 文字配列
	 * @param start 文字配列内の開始位置
	 * @param length 文字配列から使用される文字数
	 * @return true=空白文字以外が含まれていない, false=空白文字以外が含まれている
	 */
	protected static boolean isEmptyCharArray(char[] ch, int start, int length) {
		//使用される文字数の分だけループをまわす
		for (int i = 0; i < length; i++) {
			//文字配列に空白文字以外が含まれている場合
			if (!Character.isWhitespace(ch[start + i])) {
				//空白文字以外が含まれているのでfalseを返す
				return false;
			}
		}

		//最後までチェックをくぐり抜けたのでtrueを返す
		return true;
	}

	/**
	 * 指定された文字列がnullまたは空文字列かどうかを判断します。<br>
	 *
	 * @param str 文字列
	 * @return true=nullまたは空文字列, false=nullでも空文字列でもない
	 */
	protected static boolean isBlank(String str) {
		//引数の文字列がnullまたは空文字列かどうかを判断して、結果をそのまま返す
		return ((str == null) || "".equals(str));
	}


	//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
	//オーバーライドするメソッド

	/**
	 * 文書イベントの Locator オブジェクトを受け取ります。。<br>
	 *
	 * @param locator すべての SAX 文書イベントのロケータ
	 */
	@Override
	public void setDocumentLocator(Locator locator) {
		//引数の値をフィールドに設定
		this.locator = locator;
	}

	/**
	 * 要素内の文字データの通知を受け取ります。<br>
	 * このクラスの実装では、文字配列の使用範囲に空白文字以外が含まれている場合は例外を投げます。<br>
	 *
	 * @param ch 文字配列
	 * @param start 文字配列内の開始位置
	 * @param length 文字配列から使用される文字数
	 * @throws SAXException SAX例外
	 */
	@Override
	public void characters(char[] ch, int start, int length)
			throws SAXException {
		//文字配列の使用範囲に空白文字以外が含まれている場合
		if (!isEmptyCharArray(ch, start, length)) {
			//例外を投げる
			throw new SAXParseException("XML中の不正な位置に文字が含まれています。", getDocumentLocator());
		}
	}


	//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
	//Getter, Setter

	/**
	 * 結果のオブジェクトを取得します。<br>
	 *
	 * @return 結果のオブジェクト
	 */
	public Object getResult() {
		//フィールドの値をそのまま返す
		return this.result;
	}

	/**
	 * 結果のオブジェクトを設定します。<br>
	 *
	 * @param result 結果のオブジェクト
	 */
	public void setResult(Object result) {
		//引数の値をフィールドに設定
		this.result = result;
	}

	/**
	 * Locatorオブジェクトを取得します。<br>
	 *
	 * @return Locatorオブジェクト
	 */
	public Locator getDocumentLocator() {
		//フィールドの値をそのまま返す
		return this.locator;
	}

}
